#include "common.h"
#include <math.h>
#include <fstream>
#include <cstdlib>
#include <cassert>
#include <iostream>

/*==============================================================================
								Vector class
==============================================================================*/

Vector2::Vector2(): x(0.0f), y(0.0f)
{
}

Vector2::Vector2(const Vector2 &a) : x(a.x), y(a.y)
{
}

Vector2::Vector2(float x, float y) : x(x), y(y)
{
}

Vector2::~Vector2()
{
}

// Arithmetic operations
Vector2 Vector2::operator =(const Vector2& a)
{
	x = a.x;
	y = a.y;
	return *this;
}

Vector2 Vector2::operator +(const Vector2& a) const
{
	Vector2 vect(*this);
	vect += a;
	return vect;
}

Vector2 Vector2::operator -(const Vector2& a) const
{
	Vector2 vect(*this);
	vect -= a;
	return vect;
}

Vector2 Vector2::operator *(const float a) const
{
	Vector2 vect(*this);
	vect *= a;
	return vect;
}

Vector2 Vector2::operator +=(const Vector2& a)
{
	x += a.x;
	y += a.y;
	return *this;
}

Vector2 Vector2::operator -=(const Vector2& a)
{
	x -= a.x;
	y -= a.y;
	return *this;
}

Vector2 Vector2::operator *=(const float a)
{
	x *= a;
	y *= a;
	return *this;
}

Vector2 Vector2::operator /=(const float a)
{
	x /= a;
	y /= a;
	return *this;
}

// Rotates vector by given angle
void Vector2::Rotate(float angle)
{
	*this = GetRotated(angle);
}

// Returns rotated vector
Vector2 Vector2::GetRotated(float angle) const
{
	Vector2 v;
	float sin_a = sin(angle);
	float cos_a = cos(angle);
	v.x = cos_a * x - sin_a * y;
	v.y = sin_a * x + cos_a * y;
	return v;
}

// Returns length of vector
float Vector2::length() const
{
	return sqrt(x*x + y*y);
}

/*==============================================================================
							Color class
==============================================================================*/

Color::Color(): r(0), g(0), b(0), a(0)
{
}

Color::Color(const Color &c) : r(c.r), g(c.g), b(c.b), a(c.a)
{
	assert(r >= 0 && r <= 255);
	assert(g >= 0 && g <= 255);
	assert(b >= 0 && b <= 255);
	assert(a >= 0 && a <= 255);
}

Color::Color(unsigned char r, unsigned char g, unsigned char b, unsigned char a):
	r(r), g(g), b(b), a(a)
{
	assert(r >= 0 && r <= 255);
	assert(g >= 0 && g <= 255);
	assert(b >= 0 && b <= 255);
	assert(a >= 0 && a <= 255);
}

Color::~Color()
{
}

// Arithmetic operations
Color Color::operator =(const Color& c)
{
	r = c.r;
	g = c.g;
	b = c.b;
	a = c.a;
	return *this;
}

Color Color::operator +(const Color& c) const
{
	Color col(*this);
	col += c;
	return col;
}

Color Color::operator -(const Color& c) const
{
	Color col(*this);
	col -= c;
	return col;
}

Color Color::operator *(const Color& c) const
{
	Color col(*this);
	col *= c;
	return col;
}

Color Color::operator /(const Color& c) const
{
	Color col(*this);
	col /= c;
	return col;
}

Color Color::operator +=(const Color& c)
{
	r += c.r;
	g += c.g;
	b += c.b;
	a += c.a;
	return *this;
}

Color Color::operator -=(const Color& c)
{
	r -= c.r;
	g -= c.g;
	b -= c.b;
	a -= c.a;
	return *this;
}

Color Color::operator *=(const Color& c)
{
	r *= c.r;
	g *= c.g;
	b *= c.b;
	a *= c.a;
	return *this;
}

Color Color::operator /=(const Color& c)
{
	r /= c.r;
	g /= c.g;
	b /= c.b;
	a /= c.a;
	return *this;
}

// Convert color bits to range [0.0; 1.0]
void Color::Normalize(float& rf, float& gf, float& bf, float& af) const
{
	rf = r / 255.0f;
	gf = g / 255.0f;
	bf = b / 255.0f;
	af = a / 255.0f;
}

/*==============================================================================
							HSV color class
==============================================================================*/
ColorHSV::ColorHSV(): h(0.0f), s(0.0f), v(0.0f), a(0.0)
{
}

ColorHSV::ColorHSV(const ColorHSV &c)
	: h(c.h), s(c.s), v(c.v), a(c.a)
{
	if (h > 1.0f) h = 1.0f;
	if (s > 1.0f) s = 1.0f;
	if (v > 1.0f) v = 1.0f;
	if (a > 1.0f) a = 1.0f;
}

ColorHSV::ColorHSV(float h, float s, float v, float a)
	: h(h), s(s), v(v), a(a)
{
	if (h > 1.0f) h = 1.0f;
	if (s > 1.0f) s = 1.0f;
	if (v > 1.0f) v = 1.0f;
	if (a > 1.0f) a = 1.0f;
}

ColorHSV::~ColorHSV()
{
}

// Arithmetic operations
ColorHSV ColorHSV::operator =(const ColorHSV& c)
{
	h = c.h;
	s = c.s;
	v = c.v;
	a = c.a;
	return *this;
}

ColorHSV ColorHSV::operator +(const ColorHSV& c)
{
	ColorHSV col(*this);
	col += c;
	return col;
}

ColorHSV ColorHSV::operator -(const ColorHSV& c)
{
	ColorHSV col(*this);
	col -= c;
	return col;
}

ColorHSV ColorHSV::operator *(const ColorHSV& c)
{
	ColorHSV col(*this);
	col *= c;
	return col;
}

ColorHSV ColorHSV::operator /(const ColorHSV& c)
{
	ColorHSV col(*this);
	col /= c;
	return col;
}

ColorHSV ColorHSV::operator +=(const ColorHSV& c)
{
	h += c.h;
	s += c.s;
	v += c.v;
	a += c.a;
	return *this;
}

ColorHSV ColorHSV::operator -=(const ColorHSV& c)
{
	h -= c.h;
	s -= c.s;
	v -= c.v;
	a -= c.a;
	return *this;
}

ColorHSV ColorHSV::operator *=(const ColorHSV& c)
{
	h *= c.h;
	s *= c.s;
	v *= c.v;
	a *= c.a;
	return *this;
}

ColorHSV ColorHSV::operator /=(const ColorHSV& c)
{
	h /= c.h;
	s /= c.s;
	v /= c.v;
	a /= c.a;
	return *this;
}

/*==============================================================================
							Log file functions
==============================================================================*/

void LOG_INIT()
{
	std::ofstream fout(LOG_FILENAME);
	fout << "LOG INIT: " << "Initializing log file...\n";
	fout.close();
}

void LOG_ERROR(std::string error_msg)
{
	std::ofstream fout(LOG_FILENAME, std::ios::app);
	fout << "LOG ERROR: " << error_msg << '\n';
	fout.close();
}

void LOG_INFO(std::string info_msg)
{
	std::ofstream fout(LOG_FILENAME, std::ios::app);
	fout << "LOG INFO: " << info_msg << '\n';
	fout.close();
}

void LOG_CLOSE()
{
	std::ofstream fout(LOG_FILENAME, std::ios::app);
	fout << "LOG CLOSE: " << "End of log file." << '\n';
	fout.close();
}

/*==============================================================================
							Common functions
==============================================================================*/

// Gives random integer or float number (next 3 functions)
int RandomInt(int min, int max)
{
	// Dumb randomization
	return min + rand() % (max - min);
}

float RandomFloat()
{
	return (float)RandomInt(0, 32767) / 32767.0f;
} 

float RandomFloat(float min, float max)
{
	return min + RandomFloat() * (max - min);
}

// Returns min or max of two numbers
float Min(float a, float b)
{
	if (a < b) return a;
	return b;
}

float Max(float a, float b)
{
	if (a > b) return a;
	return b;
}

// Linear interpolation
float Lerp(float min, float max, float x)
{
	return min + (max - min) * x;
}

float UnLerp(float min, float max, float x)
{	
	return (x - min) / (max - min);
}

float SmoothStep(float min, float max, float x)
{
	return Lerp(min, max, x * x * (3.0f - 2.0f * x));
}

// Counts dot product of two vectors
float VectorDot(const Vector2& a, const Vector2& b)
{
	return a.x * b.x + a.y * b.y;
}

// Returns value from normalized float to byte (range: [0; 255] )
unsigned char FloatToByte(float f)
{
	return (unsigned char)(f * 255.0f);
}

// Convert RGB color to HSV
ColorHSV ToHSV(Color& rgb)
{
    ColorHSV hsv;
    hsv.a = 0.0f;
    float fr, fg, fb, fa;
	rgb.Normalize(fr, fg, fb, fa);

    float min = Min(fr, Min(fg, fb));
    float max = Max(fr, Max(fg, fb));
    hsv.v = max;
    float delta = max - min;

    if(max != 0.0f) 
	{
            hsv.s = delta / max;
    }
    else 
	{
            hsv.s = 0.0f; hsv.h = 0.0f;
            return hsv;
    }   

    if(fr == max) 
            hsv.h = (fg - fb) / delta;
    else if (fg == max)
            hsv.h = 2.0f + (fb - fr) / delta;
    else
            hsv.h = 4.0f + (fr - fg) / delta;

    hsv.h /= 6.0f;  

    if(hsv.h < 0.0f)
            hsv.h += 1.0f;
    hsv.a = fa;     

    return hsv;    
}

// Convert HSV color to RGB
Color ToRGB(ColorHSV& hsv)
{
    float f, p, q, t;
    unsigned char i, bp, bq, bt, ba, bv;

    bv = FloatToByte(hsv.v);
    ba = FloatToByte(hsv.a);
    if(hsv.s == 0.0f) 
            return Color(bv, bv, bv, ba);

    hsv.h *= 6.0f;
    i = (unsigned char)floor(hsv.h);
    f = hsv.h - i;

    p = hsv.v * (1.0f - hsv.s);
    q = hsv.v * (1.0f - (hsv.s * f));
    t = hsv.v * (1.0f - (hsv.s * (1.0f - f)));

    bp = FloatToByte(p);
    bq = FloatToByte(q);
    bt = FloatToByte(t);

    switch(i) 
	{
            case 6:
            case 0:
                    return Color(bv, bt, bp, ba);
            case 1:
                    return Color(bq, bv, bp, ba);
            case 2:
                    return Color(bp, bv, bt, ba);
            case 3:
                    return Color(bp, bq, bv, ba);
            case 4:
                    return Color(bt, bp, bv, ba);
            case 5:
                    return Color(bv, bp, bq, ba);
            default:
                    assert(false);
                    return COLOR_TRANSPARENT;
    }
}

// Get a color of the note
Color NoteColor(std::string note)
{
	std::string notee = note.erase(2, 1);
	int n = 0;

	for (int i = 0; i < 12; i++)
	{
		if (notee == note_name[i]) n = i;
	}
	assert(n >= 0 && n < 12);

	unsigned char a = 255;

	switch(n)
	{
		case 12:
		case 0:
			return Color(0, 154, 235, a);
		case 1:
			return Color(89, 194, 125, a);
		case 2:
			return Color(178, 234, 15, a);
		case 3:
			return Color(192, 193, 12, a);
		case 4:
			return Color(218, 117, 7, a);
		case 5:
			return Color(234, 75, 5, a);
		case 6:
			return Color(232, 55, 63, a);
		case 7:
			return Color(230, 20, 172, a);
		case 8:
			return Color(229, 0, 234, a);
		case 9:
			return Color(235, 53, 179, a);
		case 10:
			return Color(248, 153, 77, a);
		case 11:
			return Color(255, 210, 20, a);
		default:
			assert(false);
			return COLOR_TRANSPARENT;
	}
}

// Use linear interpolation on color
Color ColorLerp(Color col1, Color col2, float t)
{
	if (t < 0.0f) return col1;
	if (t > 1.0f) return col2;
	 
	//unsigned char bt = t * 255.0f;
	Color col;

	//col.r = col1.r + (col2.r - col1.r) * bt;
	//col.g = col1.g + (col2.g - col1.g) * bt;
	//col.b = col1.b + (col2.b - col1.b) * bt;
	//col.a = col1.a + (col2.a - col1.a) * bt;
	col.r = (int)SmoothStep((float)col1.r, (float)col2.r, t);
	col.g = (int)SmoothStep((float)col1.g, (float)col2.g, t);
	col.b = (int)SmoothStep((float)col1.b, (float)col2.b, t);
	col.a = (int)SmoothStep((float)col1.a, (float)col2.a, t);

	if (col.r > 255) col.r = 255;
	if (col.g > 255) col.g = 255;
	if (col.b > 255) col.b = 255;
	if (col.a > 255) col.a = 255;

	return col;
}

// Convert angle in degrees to angle in radians
float DegToRad(float degrees)
{
	return degrees * PI / 180.0f;
}

// Returns value in range [0; 1]
float Normalize(float value, float max_value = 1)
{
	return value * (1 / max_value);
}
